/*--------------------------------------------------------------------------
**      ComboBox.c             Twin           From:  Twin/controls
**
**
**------------------------- < License Information > ------------------------
**
**      This file was originally a part of Willows TWIN.  Willows
**  TWIN was released under a Library GPL (LGPL).  This permits
**  redistribution of this source code, provided that the full
**  TWIN license is in effect, and provided that all modifications
**  to this source code are made publicly available.
**  Please refer to Willows software (www.willows.com) or
**  LICENSE for full information.
**
**      Under Twine, this file is also protected by an LGPL.  Please
**  see LICENSE for full details on this license.
**
**
**      Copyright 1997 Willows Software, Inc.
**------------------------ < File Content Description > --------------------
**
**  Module:	 controls/ComboBox.c
**
**  Description:
**
**
**  Functions defined:
**
**------------------------- < Revision Information > -----------------------
**
**      Full Revision history at bottom of file
**
**--------------------------------------------------------------------------*/
#include <stdlib.h>
#include <string.h>
#include "windows.h"
#include "windowsx.h"
#include "wintools.h"
#include "mwconfig.h"		/* for DPRINTF*/

#define GET_WM_COMMAND_ID(wp, lp)               LOWORD(wp)
#define GET_WM_COMMAND_HWND(wp, lp)             (HWND)(lp)
#define GET_WM_COMMAND_CMD(wp, lp)              HIWORD(wp)
#define GET_WM_COMMAND_MPS(id, hwnd, cmd)       (WPARAM)MAKELONG(id, cmd), (LONG_PTR)(hwnd)
#define LOSHORT(x)                              (short int)LOWORD(x)
#define Edit_SetSel(hwndCtl, ichStart, ichEnd)  ((void)SendMessage((hwndCtl), EM_SETSEL, (ichStart), (ichEnd)))


typedef struct {
    HFONT   hFont;              /* hFont used */
    HWND    hWndParent;         /* parent window */
    UINT    nID;                /* control ID */
    WORD    wStateFlags;        /* combobox state flags */
    UINT    wStyle;             /* this is a copy of LOWORD(style) */
    BOOL    bExtended;          /* extended UI flag */
    BOOL    bRedraw;            /* MiD - redraw flag, draw only if it's 1 */
    HWND    EditControl;        /* edit/static control hWnd */
    HWND    ListBoxControl;     /* listbox control hWnd */
    RECT    ButtonRect;         /* local button rect (client) */
    RECT    ListBoxRect;        /* listbox rect (screen) */
    UINT    uHeight;            /* height of the normal state */
    WNDPROC lpfnOldStatic;      /* previous static wndproc */
    UINT    nListItems;         /* ecw */
}       COMBOBOX;

#define CWD_LPCBDATA  0
#define CBC_EDITID    1

#define CSF_CAPTUREACTIVE   0x0001
#define CSF_LOCALBUTTONDOWN 0x0002
#define CSF_BUTTONDOWN      0x0004
#define CSF_LBOXBUTTONDOWN  0x0008
#define CSF_FOCUS           0x0010      /* MiD */
#define CSF_HASDROPPED	    0x0020      /* weav */

#define SET_STATE(lp, wMask)   (lp->wStateFlags |= (wMask))
#define CLEAR_STATE(lp, wMask) (lp->wStateFlags &= ~(wMask))
#define IS_SET(lp, wMask)      (lp->wStateFlags & (wMask))

#define BOWNERDRAW(l) ((l)->wStyle & (CBS_OWNERDRAWFIXED|CBS_OWNERDRAWVARIABLE))

/*
 * Styles:
 * 
 * CBS_AUTOHSCROLL              passed to the edit control
 * CBS_DISABLENOSCROLL          passed to the listbox control
 * CBS_DROPDOWN
 * CBS_DROPDOWNLIST
 * CBS_HASSTRINGS               passed to the listbox control
 * CBS_NOINTEGRALHEIGHT         passed to the listbox control
 * CBS_OEMCONVERT               passed to the edit control
 * CBS_OWNERDRAWFIXED           passed to the listbox control
 * CBS_OWNERDRAWVARIABLE        passed to the listbox control
 * CBS_SIMPLE TODO CBS_SORT     passed to the listbox control
 * WS_VSCROLL                   passed to the listbox control
 */

/*
 * CBN_xxx messages to be added from mouse tracking...
 * CBN_SELENDCANCEL TODO
 * CBN_SELENDOK     TODO
 */

#if 0                           /* jmt: fix: no COMBOLBOX */
extern LRESULT DefLISTBOXProc(HWND, UINT, WPARAM, LPARAM);
extern LRESULT ListboxCtrlProc(HWND, UINT, WPARAM, LPARAM);
#endif

#if 0
static HPEN 
GetSysColorPen(int color)
{
    return NULL;
}
static HBRUSH 
GetSysColorBrush(int color)
{
    return NULL;
}

#endif

#if 0                           /* jmt: fix: no ownerdraw */
typedef HANDLE HCLASS32;
static HCLASS32 
FindClass(LPCSTR str, HINSTANCE hInstance)
{
    return NULL;
}

#endif

#if 0                           /* jmt: fix: no scrollbar */
static HWND 
TWIN_ConvertToSysScroll(HWND hwnd, BOOL status, LPPOINT pp)
{
    return NULL;
}

#endif

/* internal stuff */
static void CBoxDrawButton(HWND, UINT, COMBOBOX *);
static void CBoxSendMouseToLBox(COMBOBOX *, UINT, WPARAM, POINT);
static void CBoxCapture(HWND, WORD);
static void CBoxDrawEdit(COMBOBOX *, HWND, UINT);
static void CBoxDrawStatic(COMBOBOX *, HWND, UINT);     /* MiD */

/* handle specific CB messages */
static LRESULT DefCBProc(HWND, UINT, WPARAM, LPARAM);

LRESULT CALLBACK DefComboboxProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

#if 0                           /* jmt: fix: no ownerdraw */
static WNDPROC lpComboBinToNat = 0;
#endif

int 
MwRegisterComboboxControl(HINSTANCE hInstance)
{
    WNDCLASS wc;

    wc.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
    wc.lpfnWndProc = (WNDPROC) DefComboboxProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = NULL;
    wc.hCursor = 0;
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "COMBOBOX";

    return RegisterClass(&wc);
}

LRESULT CALLBACK
DefComboboxProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC     hDC;
    TEXTMETRIC tm;
#if 0                           /* jmt: fix: no ownerdraw */
    MEASUREITEMSTRUCT mis;
#endif
    COMBOBOX *lp = (COMBOBOX *) NULL;
    LRESULT rc;
    POINT   cp, cpScreen;
    UINT    uiKey;
    LPCREATESTRUCT lpcs;
#if 1                           /* jmt: fix: no WM_WINDOWPOSCHANGING */
    LPWINDOWPOS lpwp;
#endif
#if 0                           /* jmt: fix: no ownerdraw */
    HCLASS32 hComboClass32;
    LPMEASUREITEMSTRUCT lpmis;
    LPDRAWITEMSTRUCT lpdis;
    LPDELETEITEMSTRUCT lpdlis;
#endif
    DWORD   dwStyle, dwExStyle;
    WORD    wEditWidth = 0, wEditHeight;
    WORD    wCBN;
#if 0                           /* jmt: fix: no WM_SETFONT/WM_GETFONT */
    RECT    rcClient;
#endif

    rc = CB_OKAY;
    if ((uMsg != WM_CREATE /* WM_NCCREATE */ ) &&       /* (uMsg != WM_CONVERT) && */
        !(lp = (COMBOBOX *) hWnd->userdata /* GetWindowLongPtr(hWnd,CWD_LPCBDATA) */ ))
        return rc;

    switch (uMsg) {
#if 0
    case WM_SIZE:
    case WM_ENABLE:
    case WM_LBUTTONDBLCLK:
    case WM_COMPAREITEM:
    case WM_CUT:
    case WM_CLEAR:
#endif

    case WM_SETFOCUS:
        SET_STATE(lp, CSF_FOCUS);
        if ((lp->wStyle & 0x0F) == CBS_DROPDOWNLIST) {
            uiKey = (UINT) SendMessage(lp->ListBoxControl, LB_GETCURSEL, 0, 0L);
            CBoxDrawStatic(lp, hWnd, uiKey);
        }
        if (lp->EditControl) {
            SetFocus(lp->EditControl);
        }
        break;

    case WM_KILLFOCUS:
        CLEAR_STATE(lp, CSF_FOCUS);
        if ((lp->wStyle & 0x0F) == CBS_DROPDOWNLIST) {
            uiKey = (UINT) SendMessage(lp->ListBoxControl, LB_GETCURSEL, 0, 0L);
            CBoxDrawStatic(lp, hWnd, uiKey);
        }
        /*
         * Hide listbox when loosing focus to window other than our own listbox... When
         * wParam == 0 we "loose" the focus to the scrollbar in a listbox! 
         */
        if((lp->wStyle & 0x0F) != CBS_SIMPLE && wParam != (WPARAM)lp->ListBoxControl && wParam != 0)
            SendMessage(hWnd, CB_SHOWDROPDOWN, 0, 0L);
            DPRINTF(" 385: WM_KILLFOCUS\n");
            break;
#if 0                          /* jmt: fix: no WM_KEYDOWN*/
    case WM_KEYDOWN:           /* MiD 08/14/95 */
        /*
         * We have to process this message in order to show current selection in a static
         * control for certain keys. This doesn't affect combobox with an edit control,
         * since the edit traps all key messages.
         */
        {
            int     nCur = SendMessage(lp->ListBoxControl, LB_GETCURSEL, 0, 0L);
            int     nPrevCur = nCur;
            int     nCount = SendMessage(lp->ListBoxControl, LB_GETCOUNT, 0, 0L);

            if (nCount == 0)
                break;

            switch (wParam) {
            case VK_HOME:
                nCur = 0;
                break;

            case VK_END:
                nCur = nCount - 1;
                break;

            case VK_UP:
                nCur--;
                break;

            case VK_DOWN:
                nCur++;
                break;

            default:
                return 0L;
            }

            if (nCur >= nCount)
                nCur = nCount - 1;
            if (nCur < 0)
                nCur = 0;

            SendMessage(lp->ListBoxControl, LB_SETCURSEL, nCur, 0L);
            SendMessage(lp->hWndParent, WM_COMMAND, GET_WM_COMMAND_MPS(lp->nID, hWnd, CBN_SELCHANGE));
            if (nCur != nPrevCur)
                 /* ecw */ SendMessage(lp->hWndParent, WM_COMMAND, GET_WM_COMMAND_MPS(lp->nID, hWnd, CBN_SELENDOK));
            InvalidateRect(hWnd, NULL, 1);
            break;
        }
#endif                          /* WM_KEYDOWN */

    case WM_SYSCHAR:
        if (wParam == VK_DOWN) {
            PostMessage(hWnd, CB_SHOWDROPDOWN, !IS_SET(lp, CSF_CAPTUREACTIVE), 0);
            break;
        }
        /* continue in the next case... */
    case WM_KEYDOWN:
    case WM_CHAR:
    case WM_KEYUP:
        {
            int     nNewCur;
            int     nOldCur;

            if (lp->EditControl) {
                SendMessage(lp->EditControl, uMsg, wParam, lParam);
            } else {
                nOldCur = SendMessage(lp->ListBoxControl, LB_GETCURSEL, 0, 0L);
                SendMessage(lp->ListBoxControl, uMsg, wParam, lParam);
                nNewCur = SendMessage(lp->ListBoxControl, LB_GETCURSEL, 0, 0L);
                if (nNewCur != nOldCur) {
                    SendMessage(lp->hWndParent, WM_COMMAND, GET_WM_COMMAND_MPS(lp->nID, hWnd, CBN_SELCHANGE));
                    InvalidateRect(hWnd, NULL, 1);
                }
            }
            break;
        }

#if 0                           /* jmt: fix: no WM_SETREDRAW */
    case WM_SETREDRAW:
        lp->bRedraw = wParam;
        if (lp->EditControl)
            SendMessage(lp->EditControl, WM_SETREDRAW, wParam, lParam);
        if (lp->ListBoxControl)
            SendMessage(lp->ListBoxControl, WM_SETREDRAW, wParam, lParam);
        break;
#endif
    case WM_CREATE:            /* WM_NCCREATE: */
        lp = (COMBOBOX *) malloc(sizeof(COMBOBOX));
        memset((LPSTR) lp, '\0', sizeof(COMBOBOX));

        /* save ptr to internal structure */
        hWnd->userdata = (ULONG_PTR)lp;    
        lpcs = (LPCREATESTRUCT) lParam;

        /* fill in the internal structure */
        lp->bRedraw = 1;
        lp->wStateFlags = 0;
        lp->wStyle = (UINT) LOWORD(lpcs->style);
        if (!BOWNERDRAW(lp))
            lp->wStyle |= CBS_HASSTRINGS;
        lp->bExtended = TRUE;
        lp->hFont = 0;
        lp->hWndParent = lpcs->hwndParent;
        lp->nID = (UINT) lpcs->hMenu;        //OK:  Not pointer, control id always passed as UINT.

            dwStyle = GetWindowLong(hWnd, GWL_STYLE);
        dwStyle &= ~(WS_VSCROLL | WS_HSCROLL | WS_BORDER | WS_DLGFRAME | WS_THICKFRAME);
        SetWindowLong(hWnd, GWL_STYLE, dwStyle);
        SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) & ~WS_EX_CLIENTEDGE);


#if 0                           /* jmt: fix: no ownerdraw */
        /* calc the height of the edit/static control */
        if (0) {                /* (BOWNERDRAW(lp)) */
            mis.CtlType = ODT_COMBOBOX;
            mis.CtlID = (UINT) lpcs->hMenu; //OK:  Not pointer, control id always passed as UINT.
            mis.itemID = (UINT) - 1;
            mis.itemData = 0L;
            SendMessage(lpcs->hwndParent, WM_MEASUREITEM, (WPARAM) lpcs->hMenu, (LPARAM) & mis);
            /* * wEditHeight = (WORD)mis.itemHeight + 2; ** */
        }
#endif                          /* ownerdraw */

        /* get parent font dimensions */
        hDC = GetDC(GetParent(hWnd));
        GetTextMetrics(hDC, &tm);
        ReleaseDC(GetParent(hWnd), hDC);

        /* allow different fonts to fit, don't hard code */
        /* otherwise big fonts won't fit. */
        /* ***wEditHeight = ((tm.tmHeight - tm.tmInternalLeading)*7)/4;**** */
#if 0
        wEditHeight = tm.tmHeight + tm.tmInternalLeading * 3;
#else
        /* tmInternalLeading always 0 in mw */
        wEditHeight = tm.tmHeight + 8;
#endif

        lp->uHeight = (UINT) wEditHeight;

        if ((lp->wStyle & 0x0F) != CBS_SIMPLE) {
            lp->ButtonRect.top = +GetSystemMetrics(SM_CYBORDER) * 2;
            lp->ButtonRect.left = lpcs->cx - 1 - GetSystemMetrics(SM_CXVSCROLL);
            lp->ButtonRect.right = lpcs->cx;
            lp->ButtonRect.bottom = wEditHeight - GetSystemMetrics(SM_CYBORDER) * 2;
            /* for CBS_DROPDOWN/DROPDOWNLIST resize the window  */
            SetWindowPos(hWnd, 0, lpcs->x, lpcs->y, lpcs->cx, (int)wEditHeight,
                         SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW);
        } else
            SetRectEmpty(&lp->ButtonRect);

        if ((lp->wStyle & 0xf) != CBS_DROPDOWNLIST) {   /* EDIT field - calc edit control
                                                         * style */
            dwStyle = WS_CHILD | WS_VISIBLE | WS_BORDER;
            if (lp->wStyle & CBS_AUTOHSCROLL)
                dwStyle |= ES_AUTOHSCROLL;
            if (lp->wStyle & CBS_OEMCONVERT)
                dwStyle |= ES_OEMCONVERT;

            if ((lp->wStyle & 0x0F) == CBS_SIMPLE) {
                DPRINTF(" 528: wEditWidth = lpcs->cx=%d\n", lpcs->cx);
                wEditWidth = lpcs->cx;
            } else {            /* ?if ((lp->wStyle & 0xf) == CBS_DROPDOWN) */
                DPRINTF(" 533: wEditWidth = lp->ButtonRect.left - 5=%d;\n", lp->ButtonRect.left - 5);

                wEditWidth = lp->ButtonRect.left - 4;
                wEditHeight -= 4;
                dwStyle &= ~WS_BORDER;
            }
            /* create edit control */
            lp->EditControl = CreateWindow("EDIT", NULL, dwStyle,
                                           2, 2, wEditWidth, wEditHeight,
                                           hWnd, (HMENU) CBC_EDITID, lpcs->hInstance, (LPVOID) NULL);
        } else {
            /* CBS_DROPDOWN -- static instead of edit */
            lp->EditControl = 0;
        }
        /* listbox style */
        /* jmt: fix: no WS_EX_SAVEBITS, WS_EX_NOCAPTURE, WS_EX_POPUPMENU */
        dwExStyle = 0L;                         /* WS_EX_SAVEBITS | WS_EX_NOCAPTURE | WS_EX_POPUPMENU; */
        dwStyle = WS_BORDER | LBS_NOTIFY;       /* | LBS_COMBOLBOX; */
        if ((lp->wStyle & 0xf) == CBS_SIMPLE)
            dwStyle |= WS_VISIBLE | WS_CHILD;
        else
            dwStyle |= WS_POPUP;
#if 0
        if (lp->wStyle & CBS_DISABLENOSCROLL)
            dwStyle |= LBS_DISABLENOSCROLL;
#endif
        if (lp->wStyle & CBS_HASSTRINGS)
            dwStyle |= LBS_HASSTRINGS;
        if (lp->wStyle & CBS_NOINTEGRALHEIGHT)
            dwStyle |= LBS_NOINTEGRALHEIGHT;
        if (lp->wStyle & CBS_OWNERDRAWFIXED)
            dwStyle |= LBS_OWNERDRAWFIXED;
        if (lp->wStyle & CBS_OWNERDRAWVARIABLE)
            dwStyle |= LBS_OWNERDRAWVARIABLE;
        if (lp->wStyle & CBS_SORT)
            dwStyle |= LBS_SORT;
        if (lpcs->style & WS_VSCROLL)
            dwStyle |= WS_VSCROLL;

        /* calc listbox dimensions and position */
        if ((lp->wStyle & 0xf) == CBS_SIMPLE) {
            lp->ListBoxRect.left = 5;
            lp->ListBoxRect.top = wEditHeight - 1;
            lp->ListBoxRect.right = lpcs->cx;
            lp->ListBoxRect.bottom = lpcs->cy - 2;
        } else {
            lp->ListBoxRect.left = lpcs->x;
            lp->ListBoxRect.right = lp->ListBoxRect.left + lpcs->cx - 1;
            lp->ListBoxRect.top = lpcs->y + wEditHeight - 1;
            lp->ListBoxRect.bottom = lp->ListBoxRect.top + lpcs->cy + 1;
            if ((lp->wStyle & 0x0F) == CBS_DROPDOWN) {
                lp->ListBoxRect.left += 5;
            }
        }
#ifdef LATER
        cp.x = ((lp->wStyle & 0xf) == CBS_DROPDOWNLIST) ? 0 : 5;
        cp.y = wEditHeight - 1;
        if ((lp->wStyle & 0xf) != CBS_SIMPLE)
            ClientToScreen(hWnd, &cp);
        lp->ListBoxRect.left = cp.x;
        lp->ListBoxRect.top = cp.y;
        lp->ListBoxRect.right = cp.x + lpcs->cx;
        if ((lp->wStyle & 0xf) != CBS_DROPDOWNLIST)
            lp->ListBoxRect.right -= 5;
        lp->ListBoxRect.bottom = lp->ListBoxRect.top + lpcs->cy -
            wEditHeight + 1;
#endif

        lp->ListBoxControl = CreateWindowEx(dwExStyle, "LISTBOX",       /* "COMBOLBOX", */
                                            NULL, dwStyle,
                                            lp->ListBoxRect.left, lp->ListBoxRect.top,
                                            lp->ListBoxRect.right - lp->ListBoxRect.left,
                                            lp->ListBoxRect.bottom - lp->ListBoxRect.top,
                                            hWnd, 0, lpcs->hInstance, (LPVOID) NULL);
#if MWCLIENT
        MwLowerWindow(lp->ListBoxControl);
#endif
#ifdef  LATER
        /* Microsoft Word 6.0 wants to see COMBOLBOX on top */
        /* of Z-order... */
        if (dwStyle & WS_POPUP) {
            SetWindowPos(lp->ListBoxControl, HWND_TOP, 0, 0, 0, 0,
                         SWP_NOREDRAW | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
        }
#endif

#if 0                           /* jmt: fix: no HWND32(LPWININFO) */
        /*
         * physically expand client window, if there is a scroll style
         */
        if (lpcs->style & WS_VSCROLL) {
            HWND32  hWnd32 = GETHWND32(hWnd);

            SetRectEmpty(&hWnd32->rcNC);

            hWnd32->wWidth = (WORD) hWnd32->rWnd.right - hWnd32->rWnd.left;
            hWnd32->wHeight = (WORD) hWnd32->rWnd.bottom - hWnd32->rWnd.top;
            RELEASEWININFO(hWnd32);
        }
#endif
        /*
         * *   Finally turn off border drawing and WM_?SCROLL styles to prevent creation *
         * of system scrollbars.
         */
        lp->nListItems = 0;
        return TRUE;

    case WM_DESTROY:           /* WM_NCDESTROY: */
        if (IsWindow(lp->ListBoxControl))
            DestroyWindow(lp->ListBoxControl);
        if (IsWindow(lp->EditControl))
            DestroyWindow(lp->EditControl);
        free((LPSTR) lp);
        return 0L;

    case WM_GETDLGCODE:
        return DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS;

        /* jmt: twine->mwin bug fixed: */
    case WM_NCLBUTTONDOWN:     /* jmt: a must */
#if 0                           /* twine->mw buggy */
    case WM_LBUTTONDOWN:
#endif
        if ((lp->wStyle & 0xf) == CBS_SIMPLE)
            break;

        cp.x = (int)(short)LOWORD(lParam);
        cp.y = (int)(short)HIWORD(lParam);
#if 1                           /* WM_NCLBUTTONDOWN: */
        ScreenToClient(hWnd, &cp);      /* jmt: a must */
#endif
        if (!IS_SET(lp, CSF_CAPTUREACTIVE)) {   /* no listbox yet */
            /* click on a button or anywhere if it's dropdown combo */
            if (PtInRect(&lp->ButtonRect, cp) ||
                (lp->wStyle & 0x0F) == CBS_DROPDOWNLIST) {
                if (PtInRect(&lp->ButtonRect, cp))
                    CBoxDrawButton(hWnd, 1, lp);

                cp.x = ((lp->wStyle & 0xf) != CBS_DROPDOWNLIST) ? 5 : 0;
                cp.y = lp->uHeight - 1;

                ClientToScreen(hWnd, &cp);

                DPRINTF(" (1)lp->ListBoxRect:(%d,%d,%d,%d)\n", lp->ListBoxRect.left, lp->ListBoxRect.top,
                        lp->ListBoxRect.right, lp->ListBoxRect.bottom);

                OffsetRect(&lp->ListBoxRect, cp.x - lp->ListBoxRect.left, cp.y - lp->ListBoxRect.top);

                DPRINTF(" (2)lp->ListBoxRect:(%d,%d,%d,%d)\n", lp->ListBoxRect.left, lp->ListBoxRect.top,
                        lp->ListBoxRect.right, lp->ListBoxRect.bottom);

                SetWindowPos(lp->ListBoxControl, HWND_TOP, cp.x, cp.y, 0, 0,
                             SWP_NOSIZE | /* SWP_NOZORDER | */ SWP_NOACTIVATE);

                SendMessage(lp->hWndParent, WM_COMMAND, GET_WM_COMMAND_MPS(lp->nID, hWnd, CBN_DROPDOWN));
                /* ECW   added following conditional...  4/4/96 */
                /* JMT   following conditional is a must for microwindows  8/14/2k */
                if (1) {        /* -(!IS_SET(lp, CSF_HASDROPPED)) jmt: a must */
                    /* ??first time it drops down, size it to hold all items?? */

                    int     nitems = SendMessage(lp->ListBoxControl, LB_GETCOUNT, 0, 0L);
#if 0
                    /* resize if too small, in this case, also do too long */
                    if (lp->ListBoxRect.bottom - lp->ListBoxRect.top <
                        ((lp->uHeight - 2) * nitems)) {
#endif
                        nitems = (nitems > 12 ? 12 : nitems);   /* a dozen, max */

#if 0                           /* twine->mw buggy? */
                        lp->ListBoxRect.bottom = lp->ListBoxRect.top + ((lp->uHeight - 2) * nitems);
#endif
                        DPRINTF(" (2.5)lp->ListBoxRect:(%d,%d,%d,%d)\n", lp->ListBoxRect.left, lp->ListBoxRect.top,
                                lp->ListBoxRect.right, lp->ListBoxRect.bottom);

                        /* jmt: twine->mwin bug fixed: */
                        DPRINTF(" 706: fixed: SetWindowPos(lp->ListBoxControl,,%d,%d,...)\n", cp.x, cp.y);
#if 0                           /* twine->mwin bug */
                        SetWindowPos(lp->ListBoxControl, HWND_TOP, 0, 0,
                                     lp->ListBoxRect.right - lp->ListBoxRect.left,
                                     lp->ListBoxRect.bottom - lp->ListBoxRect.top,
                                     SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
#else                           /* jmt: twine->mwin bug fixed: */
                        SetWindowPos(lp->ListBoxControl, HWND_TOP, cp.x, cp.y,
                                     lp->ListBoxRect.right - lp->ListBoxRect.left,
                                     lp->ListBoxRect.bottom - lp->ListBoxRect.top,
                                     SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
#endif

#if 0
                    }
#endif
                    SET_STATE(lp, CSF_HASDROPPED);
                }
                /* End of addition */
                ShowWindow(lp->ListBoxControl, SW_SHOW);
#if 0                           /* orig(twine) */
                SetFocus(lp->ListBoxControl);
#else                           /* jmt: mwclient */
                SetForegroundWindow(lp->ListBoxControl);
#endif
                CBoxCapture(hWnd, 1);
                SET_STATE(lp, CSF_CAPTUREACTIVE);
                SET_STATE(lp, CSF_BUTTONDOWN);
            }
        } else {                /* there is a listbox visible */
            HWND    hwndNewFocus = 0;

            cpScreen = cp;

            if ((lp->wStyle & 0xf) != CBS_SIMPLE) {
                ClientToScreen(hWnd, &cpScreen);
                hwndNewFocus = WindowFromPoint(cpScreen);
            }
            DPRINTF(" (3)lp->ListBoxRect:(%d,%d,%d,%d)\n", lp->ListBoxRect.left, lp->ListBoxRect.top,
                    lp->ListBoxRect.right, lp->ListBoxRect.bottom);

            if (PtInRect(&lp->ListBoxRect, cpScreen)) {
                CBoxSendMouseToLBox(lp, WM_LBUTTONDOWN, wParam, cpScreen);
            } else {
                if (PtInRect(&lp->ButtonRect, cp))
                    CBoxDrawButton(hWnd, 0, lp);
                if ((lp->wStyle & 0x0F) == CBS_DROPDOWN && hwndNewFocus == lp->EditControl)
                     /* don't close listbox */ ;
                else {
                    SendMessage(lp->hWndParent, WM_COMMAND, GET_WM_COMMAND_MPS(lp->nID, hWnd, CBN_CLOSEUP));

                    DPRINTF(" 802: (hide) SetWindowPos(lp->ListBoxControl, , 0, 0, 0, 0,..)\n");

                    SetWindowPos(lp->ListBoxControl, 0, 0, 0, 0, 0,
                                 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_HIDEWINDOW);
#if MWCLIENT
                    MwLowerWindow(lp->ListBoxControl);
#endif
                    CBoxCapture(hWnd, 0);
                    CLEAR_STATE(lp, CSF_BUTTONDOWN);
                }
                CLEAR_STATE(lp, CSF_CAPTUREACTIVE);

                if (hwndNewFocus && hwndNewFocus != hWnd) {
                    ScreenToClient(hwndNewFocus, &cpScreen);
                    SetFocus(hwndNewFocus);

                    SendMessage(hwndNewFocus, WM_LBUTTONDOWN, wParam, MAKELONG(cpScreen.x, cpScreen.y));
                }
            }                   /* !(PtInRect(&lp->ListBoxRect, cpScreen)) */
        }
        break;

        /* jmt: twine->mwin bug fixed: */
    case WM_NCMOUSEMOVE:
#if 0                           /* jmt: twine->mw buggy */
    case WM_MOUSEMOVE:
#endif
        if (!IS_SET(lp, CSF_BUTTONDOWN) && ((lp->wStyle & 0xf) == CBS_SIMPLE))
            break;

        cp.x = (int)(short)LOWORD(lParam);
        cp.y = (int)(short)HIWORD(lParam);
#if 1                           /* WM_NCMOUSEMOVE: */
        ScreenToClient(hWnd, &cp);      /* jmt: a must */
#endif

        if (IS_SET(lp, CSF_CAPTUREACTIVE)) {
            if (PtInRect(&lp->ButtonRect, cp)) {
                if (!IS_SET(lp, CSF_LOCALBUTTONDOWN))
                    CBoxDrawButton(hWnd, 1, lp);
                break;
            }
            if ((lp->wStyle & 0xf) != CBS_SIMPLE)
                ClientToScreen(hWnd, &cp);
#if 0                           /* don't hand off mouse move messages to listbox */
            if (PtInRect(&lp->ListBoxRect, cp)) {
                CBoxSendMouseToLBox(lp, WM_MOUSEMOVE, wParam, cp);
            }
#endif
            if (IS_SET(lp, CSF_LOCALBUTTONDOWN) && ((lp->wStyle & 0xf) != CBS_SIMPLE))
                CBoxDrawButton(hWnd, 0, lp);
        }
        break;

        /* jmt: twine->mwin bug fixed: */
    case WM_NCLBUTTONUP:
#if 0                           /* twine->mw buggy */
    case WM_LBUTTONUP:
#endif
        if (!IS_SET(lp, CSF_CAPTUREACTIVE))
            break;

        cp.x = (int)(short)LOWORD(lParam);
        cp.y = (int)(short)HIWORD(lParam);
#if 1                           /* WM_NCLBUTTONUP */
        ScreenToClient(hWnd, &cp);      /* jmt: a must */
#endif

        CLEAR_STATE(lp, CSF_BUTTONDOWN);

        if (PtInRect(&lp->ButtonRect, cp))
            /* (lp->wStyle & 0x0F) == CBS_DROPDOWNLIST) */
        {
            if (PtInRect(&lp->ButtonRect, cp))
                CBoxDrawButton(hWnd, 0, lp);
            if (IS_SET(lp, CSF_LBOXBUTTONDOWN)) {
                if ((lp->wStyle & 0xf) != CBS_SIMPLE)
                    ClientToScreen(hWnd, &cp);
                CBoxSendMouseToLBox(lp, WM_LBUTTONUP, wParam, cp);
                CLEAR_STATE(lp, CSF_LBOXBUTTONDOWN);
            }
            break;
        }
        if ((lp->wStyle & 0xf) != CBS_SIMPLE)
            ClientToScreen(hWnd, &cp);

        if (PtInRect(&lp->ListBoxRect, cp)) {
            uiKey = (UINT) SendMessage(lp->ListBoxControl, LB_GETCURSEL, 0, 0);
            if (uiKey != (UINT) LB_ERR) {
                if (lp->EditControl) {
                    SetFocus(lp->EditControl);

                    CBoxDrawEdit(lp, hWnd, uiKey);
                } else {
                    SetFocus(hWnd);

                    CBoxDrawStatic(lp, hWnd, uiKey);
                }

                /* LATER check the WS_EX_NOPARENTNOTIFY bit in ext style. */
                 /* ecw */ SendMessage(lp->hWndParent, WM_COMMAND, GET_WM_COMMAND_MPS(lp->nID, hWnd, CBN_SELENDOK));
                SendMessage(lp->hWndParent, WM_COMMAND, GET_WM_COMMAND_MPS(lp->nID, hWnd, CBN_CLOSEUP));

                DPRINTF(" 844: (hide) SetWindowPos(lp->ListBoxControl, , 0, 0, 0, 0,..)\n");

                SetWindowPos(lp->ListBoxControl, 0,
                             0, 0, 0, 0,
                             SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_HIDEWINDOW);

                CBoxCapture(hWnd, 0);
                CLEAR_STATE(lp, CSF_CAPTUREACTIVE);
            }                   /* uiKey ok */
            CBoxSendMouseToLBox(lp, WM_LBUTTONUP, wParam, cp);
            CLEAR_STATE(lp, CSF_LBOXBUTTONDOWN);
#if MWCLIENT
            MwLowerWindow(lp->ListBoxControl);
#endif
        } else
            /*
             * clicked somewhere outside button or listbox - * the listbox should stay
             * intact... MiD
         */ if (IS_SET(lp, CSF_LBOXBUTTONDOWN)) {
            if ((lp->wStyle & 0xf) != CBS_SIMPLE)
                ClientToScreen(hWnd, &cp);
            CBoxSendMouseToLBox(lp, WM_LBUTTONUP, wParam, cp);
            CLEAR_STATE(lp, CSF_LBOXBUTTONDOWN);
        }
        break;

    case WM_ERASEBKGND:
        return 1L;

    case WM_PAINT:
        hDC = BeginPaint(hWnd, &ps);
        EndPaint(hWnd, &ps);

        if (!IsWindowVisible(hWnd) || !lp->bRedraw)
            return 0L;

        if ((lp->wStyle & 0xf) != CBS_SIMPLE)
            CBoxDrawButton(hWnd, IS_SET(lp, CSF_LOCALBUTTONDOWN), lp);
        uiKey = (UINT) SendMessage(lp->ListBoxControl, LB_GETCURSEL, 0, 0);
        if (lp->EditControl)
            CBoxDrawEdit(lp, hWnd, uiKey);
        else
            CBoxDrawStatic(lp, hWnd, uiKey);
        return 0L;

    case WM_COMMAND:
        if (GET_WM_COMMAND_ID(wParam, lParam) == CBC_EDITID) {
            DPRINTF("EDIT command %d\n", (WORD) GET_WM_COMMAND_CMD(wParam, lParam));
            /* edit/static control notifications */
            switch ((short)GET_WM_COMMAND_CMD(wParam, lParam)) {
            case EN_SETFOCUS:
#ifdef  LATER
                wCBN = CBN_SETFOCUS;
#else
                wCBN = 0;
#endif
                break;
            case EN_KILLFOCUS:
                wCBN = CBN_KILLFOCUS;
                break;
            case EN_CHANGE:
                {
                    int     index = 0;
                    char    sz[128];
                    /*
                     * Advance listbox selection until there is string match.
					 * One first mismatch listbox advances to its first item.
                     */
                    SendMessage(lp->EditControl, WM_GETTEXT, sizeof(sz) - 1, (LPARAM) sz);
                    if ( /* l */ strlen(sz) > 0 /* L */ )
                        index = (int)SendMessage(lp->ListBoxControl, LB_FINDSTRING, -1, (LPARAM) sz);
                    if (index == LB_ERR)
                        index = 0;
                    SendMessage(lp->ListBoxControl, LB_SETTOPINDEX, index, 0L);
                    wCBN = CBN_EDITCHANGE;
                    break;
                }
            case EN_UPDATE:
                wCBN = CBN_EDITUPDATE;
                break;
            case EN_ERRSPACE:
                wCBN = CBN_ERRSPACE;
                break;
            default:
                wCBN = 0;
                break;
            }
            if (wCBN)
                return SendMessage(lp->hWndParent, WM_COMMAND, GET_WM_COMMAND_MPS(lp->nID, hWnd, wCBN));
            else
                return rc;
        }
        if (GET_WM_COMMAND_ID(wParam, lParam) == 0) {
            DPRINTF("LIST command %d\n", (WORD) GET_WM_COMMAND_CMD(wParam, lParam));
            /* listbox notifications */
            switch ((short)GET_WM_COMMAND_CMD(wParam, lParam)) {
            case LBN_ERRSPACE:
                wCBN = CBN_ERRSPACE;
                break;
            case LBN_SELCHANGE:
                if ((lp->wStyle & 0xf) == CBS_SIMPLE) {
                    uiKey = (UINT) SendMessage(lp->ListBoxControl, LB_GETCURSEL, 0, 0);
                    if (uiKey != (UINT) LB_ERR)
                        if (lp->EditControl)
                            CBoxDrawEdit(lp, hWnd, uiKey);
                }
                CBoxCapture(hWnd, 1);   /* capture mouse from listbox again */
                wCBN = CBN_SELCHANGE;
                break;
            case LBN_DBLCLK:
                wCBN = CBN_DBLCLK;
                break;
            case LBN_SELCANCEL:/* TODO */
                wCBN = 0;
                break;
            case LBN_SETFOCUS:
                wCBN = CBN_SETFOCUS;
                break;
            case LBN_KILLFOCUS:
                wCBN = CBN_KILLFOCUS;
                break;
            default:
                wCBN = 0;
                break;
            }
            if (wCBN)
                return SendMessage(lp->hWndParent, WM_COMMAND, GET_WM_COMMAND_MPS(lp->nID, hWnd, wCBN));
            else
                return rc;
        }
        break;

    case WM_GETTEXT:
        if (lp->EditControl)
            return SendMessage(lp->EditControl, uMsg, wParam, lParam);
        else if (lp->ListBoxControl) {
            WPARAM  sel, len;

            sel = (WPARAM) SendMessage(lp->ListBoxControl, LB_GETCURSEL, 0, 0);
            if (sel != (WPARAM) LB_ERR) {
                len = (WPARAM) SendMessage(lp->ListBoxControl, LB_GETTEXTLEN, 0, 0);
                if (len <= wParam)
                    return SendMessage(lp->ListBoxControl, LB_GETTEXT, sel, lParam);
            }
        }
        return CB_ERR;

    case WM_GETTEXTLENGTH:
        if (lp->EditControl)
            return SendMessage(lp->EditControl, uMsg, wParam, lParam);
        else if (lp->ListBoxControl) {
            WPARAM  sel;

            sel = (WPARAM) SendMessage(lp->ListBoxControl, LB_GETCURSEL, 0, 0);
            if (sel != (WPARAM) LB_ERR)
                return SendMessage(lp->ListBoxControl, LB_GETTEXTLEN, sel, 0);
        }
        return CB_ERR;

    case WM_SETTEXT:
        if (lp->EditControl)
            return SendMessage(lp->EditControl, uMsg, wParam, lParam);
        return CB_ERR;

#if 0                           /* jmt: fix: no WM_SETFONT/WM_GETFONT */
    case WM_SETFONT:
        lp->hFont = (HFONT) wParam;

        hDC = GetDC(hWnd);
        SelectObject(hDC, lp->hFont);
        GetTextMetrics(hDC, &tm);
        ReleaseDC(hWnd, hDC);
        wEditHeight = tm.tmHeight + 3 * tm.tmInternalLeading;

        if (wEditHeight == lp->uHeight)
            return 0L;

        lp->uHeight = (UINT) wEditHeight;
        lp->ButtonRect.bottom = wEditHeight;
        /*
         * *   The following SetWindowPos causes WM_WINDOWPOSCHANGING message *   where
         * child windows are resized and/or moved.
         */
        ShowWindow(hWnd, SW_HIDE);
        GetClientRect(hWnd, &rcClient);
        if ((lp->wStyle & 0xf) != CBS_SIMPLE)
            SetWindowPos(hWnd, 0,
                         0, 0, rcClient.right, (int)wEditHeight,
                         SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW);
        else
            SetWindowPos(hWnd, 0,
                         0, 0, rcClient.right, (int)wEditHeight + lp->ListBoxRect.bottom - lp->ListBoxRect.top + 1,
                         SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW);
        ShowWindow(hWnd, SW_SHOWNA);

        if (lp->EditControl)
            SendMessage(lp->EditControl, WM_SETFONT, wParam, lParam);
        SendMessage(lp->ListBoxControl, WM_SETFONT, wParam, lParam);

        if (LOWORD(lParam))
            RedrawWindow(hWnd, (const RECT *)0, (HRGN) 0,
                         RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW);
        return (LRESULT) 0;

    case WM_GETFONT:
        return lp->hFont;
#endif                          /* WM_SETFONT/WM_GETFONT */

    case WM_MOVE:              /* WM_WINDOWPOSCHANGING: */
#if 0
        //lpwp = (LPWINDOWPOS) lParam;
        pp.x = LOWORD(lParam);
        pp.y = HIWORD(lParam);
#endif
        if (1) {                /* (lpwp) */
            if (1) {            /* (!(lpwp->flags & SWP_NOSIZE)) */
                lp->ButtonRect.right = (hWnd->winrect.right - hWnd->winrect.left);      /* lpwp->cx; */
                if ((lp->wStyle & 0xf) == CBS_SIMPLE)
                    lp->ButtonRect.left = lp->ButtonRect.right;
                else
                    lp->ButtonRect.left = (hWnd->winrect.right - hWnd->winrect.left) /* lpwp->cx */ -1 -
                        GetSystemMetrics(SM_CXVSCROLL);

                if (lp->EditControl) {
                    WORD    wEditTop = 0, wEditLeft = 0, wEditHeight = lp->uHeight;
                    wEditWidth = lp->ButtonRect.left + 1;
                    if ((lp->wStyle & 0xf) == CBS_SIMPLE)
                        wEditWidth--;
                    if ((lp->wStyle & 0xf) == CBS_DROPDOWN) {
                        wEditWidth -= 3;
                        wEditTop = 2;
                        wEditLeft = 2;
                        wEditHeight -= 4;
                    }
                    SetWindowPos(lp->EditControl, (HWND) 0,
                                 wEditTop, wEditLeft,
                                 wEditWidth, wEditHeight,
                                 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
                }
                if (lp->ListBoxControl) {
                    if ((lp->wStyle & 0x0F) == CBS_SIMPLE) {
                        lp->ListBoxRect.left = 5;
                        lp->ListBoxRect.top = lp->uHeight - 1;
                        lp->ListBoxRect.right = (hWnd->winrect.right - hWnd->winrect.left);     /* lpwp->cx; */
                        lp->ListBoxRect.bottom = (hWnd->winrect.bottom - hWnd->winrect.top) /* lpwp->cy */ -2;
                    } else {
                        POINT   cp;
                        cp.x = 0;
                        cp.y = lp->uHeight - 1;
                        ClientToScreen(hWnd, &cp);
                        OffsetRect(&lp->ListBoxRect, cp.x - lp->ListBoxRect.left, cp.y - lp->ListBoxRect.top);

                        lp->ListBoxRect.right = lp->ListBoxRect.left + (hWnd->winrect.right - hWnd->winrect.left) /* lpwp->cx */ ;
                        if ((lp->wStyle & 0xf) != CBS_DROPDOWNLIST)
                            lp->ListBoxRect.right -= 5;
                    }

                    SetWindowPos(lp->ListBoxControl, (HWND) 0,
                                 lp->ListBoxRect.left, lp->ListBoxRect.top,
                                 lp->ListBoxRect.right - lp->ListBoxRect.left,
                                 lp->ListBoxRect.bottom - lp->ListBoxRect.top,
                                 SWP_NOACTIVATE | SWP_NOZORDER);
                }
#if 0                           /* jmt: fix: no WM_WINDOWPOSCHANGING */
                /* the height of the normal state stays the same */
                if ((lp->wStyle & 0xf) != CBS_SIMPLE)
                    lpwp->cy = (int)lp->uHeight;
#endif
            }
        }
        return (LRESULT) 0;

    case WM_WINDOWPOSCHANGED:
        DefWindowProc(hWnd, uMsg, wParam, lParam);
        lpwp = (LPWINDOWPOS) lParam;
        if (lpwp) {
            if (!(lpwp->flags & SWP_NOSIZE))    /* TODO */
#if 0
                RedrawWindow(hWnd, (const RECT *)0, (HRGN) 0, RDW_INVALIDATE | RDW_ERASE);
#else
                InvalidateRect(hWnd, NULL, TRUE);
#endif
        }
        return (LRESULT) 0;

#if 0                           /* jmt: fix: no ownerdraw */
        /*
         * ownerdraw stuff
         */
    case WM_DRAWITEM:
        lpdis = (LPDRAWITEMSTRUCT) lParam;
        lpdis->CtlType = ODT_COMBOBOX;
        lpdis->CtlID = lp->nID;
        lpdis->hwndItem = hWnd;
        return SendMessage(lp->hWndParent, WM_DRAWITEM, (WPARAM) lp->nID, lParam);

    case WM_MEASUREITEM:
        lpmis = (LPMEASUREITEMSTRUCT) lParam;
        lpmis->CtlType = ODT_COMBOBOX;
        lpmis->CtlID = lp->nID;
        return SendMessage(lp->hWndParent, WM_MEASUREITEM, (WPARAM) lp->nID, lParam);

    case WM_DELETEITEM:
        lpdlis = (LPDELETEITEMSTRUCT) lParam;
        lpdlis->CtlType = ODT_COMBOBOX;
        lpdlis->CtlID = lp->nID;
        lpdlis->hwndItem = hWnd;
        return SendMessage(lp->hWndParent, WM_DELETEITEM, (WPARAM) lp->nID, lParam);

    case WM_CONVERT:
        if (!lpComboBinToNat) {
            hComboClass32 = FindClass("COMBOBOX", 0);
            lpComboBinToNat = (WNDPROC) GetClassHandleLong(hComboClass32, GCL_BINTONAT);
        }
        if (lpComboBinToNat)
            return lpComboBinToNat(hWnd, uMsg, wParam, lParam);
        else
            return (LRESULT) 0;
#endif                          /* ownerdraw */

    default:
        return DefCBProc(hWnd, uMsg, wParam, lParam);
    }
    return rc;
}

/*
 * ********************************************************************** *
 * 
**********************************************************************
 */
static LRESULT 
DefCBProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    int     len, index;
    COMBOBOX *lp;
    char   *selection;
    int     rc;
    POINT   cp;

    lp = (COMBOBOX *) hWnd->userdata /* GetWindowLongPtr(hWnd,CWD_LPCBDATA) */ ;
    switch (uMsg) {
        /*
         * messages specific to the list box control
         */
    case CB_ADDSTRING:
        lp->nListItems++;       /* shd. test for successful return */
        return SendMessage(lp->ListBoxControl, LB_ADDSTRING,
                           wParam, lParam);

    case CB_DELETESTRING:
        if (lp->nListItems)
            lp->nListItems--;
        return SendMessage(lp->ListBoxControl, LB_DELETESTRING, wParam, lParam);

    case CB_DIR:
        return SendMessage(lp->ListBoxControl, LB_DIR, wParam, lParam);

    case CB_FINDSTRING:
        return SendMessage(lp->ListBoxControl, LB_FINDSTRING, wParam, lParam);

    case CB_FINDSTRINGEXACT:
        return SendMessage(lp->ListBoxControl, LB_FINDSTRINGEXACT, wParam, lParam);

    case CB_GETCOUNT:
        return SendMessage(lp->ListBoxControl, LB_GETCOUNT, wParam, lParam);

    case CB_GETCURSEL:
        return SendMessage(lp->ListBoxControl, LB_GETCURSEL, wParam, lParam);

    case CB_GETITEMDATA:
        return SendMessage(lp->ListBoxControl, LB_GETITEMDATA, wParam, lParam);

    case CB_GETITEMHEIGHT:
        return SendMessage(lp->ListBoxControl, LB_GETITEMHEIGHT, wParam, lParam);

    case CB_GETLBTEXT:
        return SendMessage(lp->ListBoxControl, LB_GETTEXT, wParam, lParam);

    case CB_GETLBTEXTLEN:
        return SendMessage(lp->ListBoxControl, LB_GETTEXTLEN, wParam, lParam);

    case CB_INSERTSTRING:
        return SendMessage(lp->ListBoxControl, LB_INSERTSTRING, wParam, lParam);

    case CB_SETITEMDATA:
        return SendMessage(lp->ListBoxControl, LB_SETITEMDATA, wParam, lParam);

        /*
         * messages specific to the edit control
         */
    case CB_GETEDITSEL:
        return SendMessage(lp->EditControl, EM_GETSEL, 0, 0);

    case CB_LIMITTEXT:
        return SendMessage(lp->EditControl, EM_LIMITTEXT, wParam, lParam);

    case CB_SETEDITSEL:
        return SendMessage(lp->EditControl, EM_SETSEL, wParam, lParam);

        /*
         * messages handled by the combobox
         */
    case CB_GETDROPPEDCONTROLRECT:
        CopyRect((LPRECT) lParam, &lp->ListBoxRect);
        break;
    case CB_GETDROPPEDSTATE:
        return IS_SET(lp, CSF_CAPTUREACTIVE);

    case CB_GETEXTENDEDUI:
        return (LRESULT) lp->bExtended;

    case CB_RESETCONTENT:
        SendMessage(lp->ListBoxControl, LB_RESETCONTENT, 0, 0);
        if (lp->EditControl)
            SendMessage(lp->EditControl, WM_SETTEXT, 0, (LPARAM) (LPSTR) "");
        break;

    case CB_SELECTSTRING:
        index = (int)SendMessage(lp->ListBoxControl, LB_SELECTSTRING, wParam, lParam);
        if (index == LB_ERR)
            return CB_ERR;

        len = (int)SendMessage(lp->ListBoxControl, LB_GETTEXTLEN, index, 0);
        if (len <= 0)
            return CB_ERR;

        selection = (LPSTR) malloc((UINT) len + 1);
        rc = (int)SendMessage(lp->ListBoxControl, LB_GETTEXT, (WPARAM) index, (LPARAM) selection);
        if (lp->EditControl)
            rc = (int)SendMessage(lp->EditControl, WM_SETTEXT, 0, (LPARAM) selection);
        else
            CBoxDrawStatic(lp, hWnd, index);
        free(selection);
        break;

    case CB_SETCURSEL:
        rc = (int)SendMessage(lp->ListBoxControl, LB_SETCURSEL, wParam, lParam);
        if (rc == LB_ERR)
            return CB_ERR;
        len = (int)SendMessage(lp->ListBoxControl, LB_GETTEXTLEN, wParam, 0);
        if (len <= 0)
            return CB_ERR;

        selection = (LPSTR) malloc((UINT) len + 1);
        rc = (int)SendMessage(lp->ListBoxControl, LB_GETTEXT, wParam, (LPARAM) selection);
        if (lp->EditControl)
            rc = (int)SendMessage(lp->EditControl, WM_SETTEXT, 0, (LPARAM) selection);
        else
            CBoxDrawStatic(lp, hWnd, wParam);
        free(selection);
        return (LRESULT) wParam;

    case CB_SETEXTENDEDUI:
        lp->bExtended = (BOOL) wParam;
        break;

    case CB_SETITEMHEIGHT:     /* TODO */
        break;

    case CB_SHOWDROPDOWN:
        if ((lp->wStyle & 0xf) == CBS_SIMPLE)
            return 1L;
        if (wParam) {
            if (IS_SET(lp, CSF_CAPTUREACTIVE))
                return 1L;

            cp.x = ((lp->wStyle & 0xf) != CBS_DROPDOWNLIST) ? 5 : 0;
            cp.y = lp->uHeight - 1;

            ClientToScreen(hWnd, &cp);
            OffsetRect(&lp->ListBoxRect, cp.x - lp->ListBoxRect.left, cp.y - lp->ListBoxRect.top);

            SetWindowPos(lp->ListBoxControl, 0,
                         cp.x, cp.y, 0, 0,
                         SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

            SendMessage(lp->hWndParent, WM_COMMAND, GET_WM_COMMAND_MPS(lp->nID, hWnd, CBN_DROPDOWN));

            DPRINTF(" 1330: SetWindowPos(lp->ListBoxControl, , 0, 0, 0, 0,..)\n");

            SetWindowPos(lp->ListBoxControl, HWND_TOP,
                         0, 0, 0, 0,
                         SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW);

            CBoxCapture(hWnd, 1);
            SET_STATE(lp, CSF_CAPTUREACTIVE);
        } else {
            if (!IS_SET(lp, CSF_CAPTUREACTIVE))
                return 1L;

            SendMessage(lp->hWndParent, WM_COMMAND, GET_WM_COMMAND_MPS(lp->nID, hWnd, CBN_CLOSEUP));
            /* test: */
            DPRINTF(" 1343: (hide) SetWindowPos(lp->ListBoxControl, , 0, 0, 0, 0,..)\n");

            SetWindowPos(lp->ListBoxControl, 0,
                         0, 0, 0, 0,
              SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_HIDEWINDOW);

            CBoxCapture(hWnd, 0);
            CLEAR_STATE(lp, CSF_CAPTUREACTIVE);
        }
        return 1L;

        /*
         * messages handled by the defwindowproc
         */
    default:
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    return CB_OKAY;
}


static void
CBoxDrawButton(HWND hWnd, UINT wState, COMBOBOX * lp)
{
    HDC     hDC;
    int     x, y;
    int     dx, dy, cx, cy;
    POINT   p3[3];
    int     shrink = 1;
#if 0                           /* jmt: fix: no LoadBitmap() */
    int     cx, cy;
    static int nWidth, nHeight;
    BITMAP  bmpCombo;
    static HBITMAP hbmpCombo = 0;
    HBITMAP hbmpOld = 0;
    HDC     hdcSrc;
    COLORREF rgbText, rgbBk;
#endif
    RECT    rc;

    hDC = GetDC(hWnd);

    GetClientRect(hWnd, &rc);
    Draw3dInset(hDC, rc.left, rc.top, lp->ButtonRect.right - rc.left + 2, rc.bottom - rc.top);

    CopyRect(&rc, &lp->ButtonRect);
    x = rc.left;
    y = rc.top;
    dx = rc.right;
    dy = rc.bottom;
    cx = dx - x;
    cy = dy - y;

    /* Button */
    FillRect(hDC, &rc, (HBRUSH) (COLOR_BTNFACE + 1));
    Draw3dUpDownState(hDC, rc.left, rc.top, cx, cy, wState);
    SelectObject(hDC, GetStockObject(BLACK_BRUSH));
    /* down */
    p3[0].x = rc.left + (cx / 2) - 1;
    p3[0].y = rc.bottom - (cy / 2) + 1;
    p3[1].x = p3[0].x - 2 - shrink;
    p3[1].y = p3[0].y - 2 - shrink;
    p3[2].x = p3[0].x + 2 + shrink;
    p3[2].y = p3[1].y;
    Polygon(hDC, p3, 3);

#if 0                           /* jmt: fix: no LoadBitmap(),GetObject() */
    if (hbmpCombo == 0) {
        hbmpCombo = LoadBitmap(0, (LPSTR) OBM_COMBO);
        GetObject(hbmpCombo, sizeof(BITMAP), (LPVOID)&bmpCombo);
        nWidth = bmpCombo.bmWidth;
        nHeight = bmpCombo.bmHeight;
    }
    /*
     * *   MiD 08/15/95 changed to mono bitmap as it is in Windows. Convert *
     * it to colors on the fly
     */
    hdcSrc = CreateCompatibleDC(hDC);
    hbmpOld = SelectObject(hdcSrc, hbmpCombo);
    /*
     * *   Source hdc ok. Prepare the target hdc, then BitBlt to it.
     */
    rgbText = SetTextColor(hDC, GetSysColor(COLOR_BTNTEXT));
    rgbBk = SetBkColor(hDC, GetSysColor(COLOR_BTNFACE));

    cx = (dx - x - nWidth) / 2;
    cy = (dy - y - nHeight) / 2;
    if (wState) {
        cx++;
        cy++;
    }
    BitBlt(hDC, x + cx, y + cy, nWidth, nHeight, hdcSrc, 0, 0, SRCCOPY);

    SetTextColor(hDC, rgbText);
    SetBkColor(hDC, rgbBk);
    SelectObject(hdcSrc, hbmpOld);
    DeleteDC(hdcSrc);
#endif                          /* BitBlt Bitmap */
    ReleaseDC(hWnd, hDC);

    if (wState)
        SET_STATE(lp, CSF_LOCALBUTTONDOWN);
    else
        CLEAR_STATE(lp, CSF_LOCALBUTTONDOWN);
}

#if 0                           /* jmt: fix: no COMBOLBOX */
LRESULT 
DefCOMBOLBOXProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
#if 0
    return DefLISTBOXProc(hWnd, msg, wParam, lParam);
#endif
    return ListboxCtrlProc(hWnd, msg, wParam, lParam);
}

#endif

static void 
CBoxSendMouseToLBox(COMBOBOX * lp, UINT uiMsg, WPARAM wParam, POINT ptScreen)
{
    POINT   pt;
    int     nNCHit;
#if 0                           /* jmt: fix: no scrollbar */
    HWND    hWndScroll;
#endif
    pt = ptScreen;
    ScreenToClient(lp->ListBoxControl, &pt);

    nNCHit = LOSHORT(SendMessage(lp->ListBoxControl, WM_NCHITTEST, 0, MAKELPARAM(ptScreen.x, ptScreen.y)));

    switch (nNCHit) {
    case HTCLIENT:
        if (uiMsg == WM_MOUSEMOVE && !IS_SET(lp, CSF_LBOXBUTTONDOWN)) {
            SendMessage(lp->ListBoxControl, WM_LBUTTONDOWN, 0, MAKELONG((WORD) pt.x, (WORD) pt.y));

            SET_STATE(lp, CSF_BUTTONDOWN | CSF_LBOXBUTTONDOWN);
        }
        SendMessage(lp->ListBoxControl, uiMsg, wParam, MAKELONG((WORD) pt.x, (WORD) pt.y));
        break;

#if 0                           /* jmt: fix: no scrollbar */
    case HTVSCROLL:
        if (0 != (hWndScroll = TWIN_ConvertToSysScroll(lp->ListBoxControl, TRUE /* vertical */ , &pt)))
            SendMessage(hWndScroll, uiMsg, wParam, MAKELONG((WORD) pt.x, (WORD) pt.y));
        break;
#endif
    default:
        break;
    }
}

static void 
CBoxCapture(HWND hWnd, WORD wFunc)
{
    static HWND hWndCapture = (HWND) 0;

    DPRINTF("CAPTURE %d\n", wFunc);
    if (wFunc) {
        hWndCapture = SetCapture(hWnd);

        SetFocus(hWnd);
    } else {
        if (!hWndCapture)
            ReleaseCapture();
        else {
#ifdef  LATER
            SetCapture(hWndCapture);
#else
            ReleaseCapture();
#endif
            hWndCapture = (HWND) 0;
        }
    }
}

static void 
CBoxDrawEdit(COMBOBOX * lp, HWND hWnd, UINT uiKey)
{
    int     nLen;
    LPVOID  lpData;
#if 0                           /* jmt: fix: no ownerdraw */
    HRGN    hRgn;
    DRAWITEMSTRUCT dis;
#endif
#if 0
     if (uiKey == (UINT)LB_ERR)
	     return;
     if (!BOWNERDRAW(lp))
#endif
    if (lp->wStyle & CBS_HASSTRINGS) {
        if (uiKey == (UINT) LB_ERR)
            return;
        nLen = (int)SendMessage(lp->ListBoxControl, LB_GETTEXTLEN, uiKey, 0L);
        if (nLen <= 0)
            return;
        lpData = (LPVOID) malloc(nLen + 1);
        SendMessage(lp->ListBoxControl, LB_GETTEXT, uiKey, (LPARAM) lpData);
        SendMessage(lp->EditControl, WM_SETTEXT, strlen(lpData), (LPARAM) lpData);
        Edit_SetSel(lp->EditControl, 0, -1);
        free((LPSTR) lpData);
    }
#if 0                           /* jmt: fix: no ownerdraw */
    else {
        dis.CtlType = ODT_COMBOBOX;
        dis.CtlID = (UINT) lp->nID;
        dis.itemID = -1;        /* used to be uiKey */
        dis.itemAction = ODA_DRAWENTIRE;
        dis.itemState = ODS_FOCUS;
        dis.hwndItem = hWnd;
        dis.itemData = 0;
        GetClientRect(lp->EditControl, &dis.rcItem);
        dis.rcItem.left += 3;
        dis.rcItem.right -= 3;
        dis.rcItem.top += 2;
        dis.rcItem.bottom -= 2;

        dis.hDC = GetDC(lp->EditControl);
        hRgn = CreateRectRgnIndirect(&dis.rcItem);
        SelectClipRgn(dis.hDC, hRgn);
        SelectObject(dis.hDC, lp->hFont);
        SendMessage(lp->hWndParent, WM_DRAWITEM, (WPARAM) (UINT) lp->nID, (LPARAM) & dis);
        ReleaseDC(lp->EditControl, dis.hDC);
        DeleteObject(hRgn);
    }
#endif                          /* ownerdraw */
}

static void 
CBoxDrawStatic(COMBOBOX * lp, HWND hWnd, UINT uiKey)
{
    int     nLen;
    HDC     hdc;
    LPVOID  lpData;
    RECT    rcClient;
    HFONT   hfonOld = 0;
#if 0                           /* jmt: fix: no ownerdraw */
    HRGN    hRgn;
    DRAWITEMSTRUCT dis;
#endif
    HBRUSH  hbrStatic, hbrOld;

    /*
     * Draw rectangle regardless of ownerdraw style...
     */
    hdc = GetDC(hWnd);
    rcClient.left = 2;
    rcClient.top = 2;
    rcClient.right = lp->ButtonRect.left;
    rcClient.bottom = lp->uHeight - 2;
    hbrStatic = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
    hbrOld = SelectObject(hdc, hbrStatic);
    SelectObject(hdc, GetStockObject(WHITE_PEN));       /* ??? COLOR_WINDOWFRAME */
    Rectangle(hdc, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
    SelectObject(hdc, hbrOld);
    DeleteObject(hbrStatic);
    ReleaseDC(hWnd, hdc);

    if (uiKey == (UINT) LB_ERR)
        return;

    /* jmt: no ownerdraw */
    if (1) {                    /* (!BOWNERDRAW(lp)) */
        /* if necessary, draw text */
        hdc = GetDC(hWnd);
        nLen = (int)SendMessage(lp->ListBoxControl, LB_GETTEXTLEN, (WPARAM) uiKey, 0L);
        if (nLen > 0) {
            lpData = (LPVOID) malloc(nLen + 1);
            SendMessage(lp->ListBoxControl, LB_GETTEXT, uiKey, (LPARAM) lpData);
            SetBkMode(hdc, TRANSPARENT);
            if (!IS_SET(lp, CSF_FOCUS)) {
                SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
                rcClient.left += 2;
            } else {
                InflateRect(&rcClient, -2, -2);
                hbrStatic = CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
                hbrOld = SelectObject(hdc, hbrStatic);
                FillRect(hdc, &rcClient, hbrStatic);
#if 0                           /* jmt: fix: no DrawFocusRect() */
                DrawFocusRect(hdc, &rcClient);
#endif
                SelectObject(hdc, hbrOld);
                DeleteObject(hbrStatic);
                SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
            }
            if (lp->hFont)
                hfonOld = SelectObject(hdc, lp->hFont);
            DrawText(hdc, (LPSTR) lpData, nLen, &rcClient, DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
            if (lp->hFont)
                SelectObject(hdc, hfonOld);
            free((LPVOID) lpData);
        }
        ReleaseDC(hWnd, hdc);
    }
#if 0                           /* jmt: fix: no ownerdraw */
    else {                      /* fill OWNERDRAWSTRUCT and send WM_DRAWITEM message */
        dis.CtlType = ODT_COMBOBOX;
        dis.CtlID = (UINT) lp->nID;
        dis.itemID = uiKey;
        dis.itemAction = ODA_DRAWENTIRE;
        dis.itemState = ODS_FOCUS;
        dis.hwndItem = hWnd;
        dis.itemData = SendMessage(lp->ListBoxControl, LB_GETITEMDATA, uiKey, 0L);
        GetClientRect(hWnd, &dis.rcItem);
        dis.rcItem.left += 3;
        dis.rcItem.right = lp->ButtonRect.left - 2;     /* do not touch button */
        dis.rcItem.top += 2;
        dis.rcItem.bottom -= 2;

        dis.hDC = GetDC(hWnd);
        hRgn = CreateRectRgnIndirect(&dis.rcItem);
        SelectClipRgn(dis.hDC, hRgn);
        SelectObject(dis.hDC, lp->hFont);
        SendMessage(lp->hWndParent, WM_DRAWITEM, (WPARAM) (UINT) lp->nID, (LPARAM) & dis);
        ReleaseDC(hWnd, dis.hDC);
        DeleteObject(hRgn);
    }
#endif                          /* ownerdraw */

}


/*------------------------- < Full Revision History > ----------------------
** Revision 1.3  2003/10/01 00:00:00  GB
** modified to works with dialogs.
**
** Revision 1.2  2001/11/06 23:35:46  greg
**
** Revision 1.1.1.1  2001/06/21 06:32:42  greg
** Microwindows pre8 with patches
**
** Revision 1.1.1.1  2001/06/05 03:44:01  root
** First import of 5/5/2001 Microwindows to CVS
**
** Revision 1.8  2000/08/14 jmt
** ported to microwin(non-client/server mode)
**
** Revision 1.7  2000/06/28 jmt
** porting to microwin
**
** Revision 1.6  2000/01/21 02:48:47  robf
** remove dead code
**
** Revision 1.5  1999/11/29 05:07:54  robf
** removed extraneous call CreateCompatibleDC
**
** Revision 1.4  1999/07/08 18:52:50  mwalsh
** Updated Comments
**
**-------------------------------------------------------------------------*/
